package com.ibiaoke.order.service;

import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.ibiaoke.common.configs.AppWeChatConfigs;
import com.ibiaoke.common.configs.GlobalConfigs;
import com.ibiaoke.common.configs.GlobalConfigsKey;
import com.ibiaoke.common.configs.WeChatConfigs;
import com.ibiaoke.common.consts.BusinessDef;
import com.ibiaoke.common.exception.ServiceException;
import com.ibiaoke.common.spring.ExceptionAspect;
import com.ibiaoke.common.utils.*;
import com.ibiaoke.customer.consts.PayTypeConst;
import com.ibiaoke.customer.consts.TradeStatusConsts;
import com.ibiaoke.customer.consts.TradeTypeConsts;
import com.ibiaoke.customer.dao.CustomerDao;
import com.ibiaoke.customer.entity.Customer;
import com.ibiaoke.customer.entity.TradeRecorder;
import com.ibiaoke.customer.service.CustomerService;
import com.ibiaoke.customer.service.DepositRecorderService;
import com.ibiaoke.customer.service.DepositService;
import com.ibiaoke.order.dao.XcxOrderDao;
import com.ibiaoke.order.entity.XcxOrder;
import com.ibiaoke.pay.BkWeChatPayClient;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jdom.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.util.*;

@Service
public class XcxPayOrderServiceImpl implements  XcxPayOrderService{
    private static Logger logger = LoggerFactory.getLogger(ExceptionAspect.class);
    @Autowired
    private CustomerService customerService;

    @Autowired
    private DepositService depositService;

    @Autowired
    private DepositRecorderService depositRecorderService;

    @Autowired
    private XcxOrderService xcxOrderService;

    @Autowired
    private XcxOrderDao xcxOrderDao;

    @Autowired
    private BkWeChatPayClient bkWeChatPayClient;

    @Autowired
    private PtOrderService ptOrderService;

    @Autowired
    private CustomerDao customerDao;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void payXcxOrderByDepositPay(XcxOrder xcxOrder, String smsCode) {
        AssertUtils.notNull(xcxOrder.getOrderId(), "请选择支付订单");
        AssertUtils.notBlank(smsCode, "验证码不能为空");
        validate(xcxOrder);
        Customer customer = customerService.getCustomerInfo(xcxOrder.getUserId());
        SmsUtil.validateMobileCode(customer.getCustomerMobile(), smsCode);
        //更改订单金额
        int i = depositService.payOrder(customer.getCustomerId(), xcxOrder.getOrderPrice());
        if (i > 0) {
            //增加更改金额日志
            depositRecorderService.insert(customer.getCustomerId(), PayTypeConst.DEPOSIT, xcxOrder.getOrderCode(), TradeTypeConsts.CONSUME, xcxOrder.getOrderPrice());
            if(xcxOrder.getOrderType().toString().equals("0")){//正常下单
                //更改 订单  1支付时间 2更新时间 3状态为 4售出数量 5分店铺商品库存
                xcxOrderService.updateOrderStatusByOnly(xcxOrder);
                //发送短信
                xcxOrderService.sendOrderMessage(xcxOrder);
            }else if(xcxOrder.getOrderType().toString().equals("1") || xcxOrder.getOrderType().toString().equals("2")){//拼团、参团下单
                //拼团回调业务逻辑
                xcxOrderService.updateOrderStatus(xcxOrder);
            }else if(xcxOrder.getOrderType().toString().equals("3")){//快捷支付
                //更改 订单 1支付时间 2更新时间 3状态
                xcxOrderDao.updatePayTimeAndUpdateTime(xcxOrder);
                xcxOrderDao.upPtOrderStatus(xcxOrder.getOrderId(),3);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> payXcxOrderByWe(Integer xcxOrderId, String openId) {
        AssertUtils.notNull(xcxOrderId, "请选择订单");
        XcxOrder xcxOrder = xcxOrderDao.selectByPrimaryKey(xcxOrderId);
        validate(xcxOrder);
        String body = "让我来果源拼团订单：" + xcxOrder.getOrderCode();
        String out_trade_no = xcxOrder.getOrderCode();
        String total_fee = String.valueOf(CashUtil.getCent(xcxOrder.getOrderPrice()));
        String trade_type = "JSAPI";
        String notify_url = GlobalConfigs.getConfigure(GlobalConfigsKey.WeChat.xcx_NOTIFY_URL);
        notify_url = notify_url.replace("BUSINESS_TYPE", "XCX_PAY_ORDER");
        return xcxWxPay(openId, body, out_trade_no, total_fee, trade_type, notify_url);
    }

    //校验订单
    public void validate(XcxOrder xcxOrder) {
        AssertUtils.notNull(xcxOrder, "请选择支付订单");
        if(xcxOrder.getOrderStatus()!=0){
            throw new ServiceException("订单付款异常");
        }
        AssertUtils.requireTrue(xcxOrder.getOrderPrice().compareTo(new BigDecimal("0.01")) >= 0,
                "支付金额不能小于0.01元");
        AssertUtils.requireTrue(xcxOrder.getCreateTime().after(DateUtils.add(Calendar.HOUR,-1))
                ,"订单超时，不能支付");
    }

    //微信退款
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object refundPayByWx(XcxOrder xcxOrder) {
        try{
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            FileInputStream instream = new FileInputStream(new File(this.getClass().getClassLoader().getResource(GlobalConfigs.getConfigure(GlobalConfigsKey.WeChat.pt_Refund_Certificate)).getPath()));
            try {
                keyStore.load(instream, WeChatConfigs.getMchID().toCharArray());
            }finally {
                instream.close();
            }
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WeChatConfigs.getMchID().toCharArray()).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
            String xml =wxPayRefund(xcxOrder.getOrderCode(),String.valueOf((int)(xcxOrder.getOrderPrice().doubleValue()*100)));

            try {
                StringEntity se = new StringEntity(xml);
                httppost.setEntity(se);
                CloseableHttpResponse responseEntry = httpclient.execute(httppost);
                try {
                    HttpEntity entity = responseEntry.getEntity();
                    if (entity != null) {
                        JSONObject result = new JSONObject();
                        SAXReader saxReader = new SAXReader();
                        Document document = saxReader.read(entity.getContent());
                        Element rootElt = document.getRootElement();
                        String resultcode ="";
                        resultcode = rootElt.elementText("result_code");
                        if(resultcode.equals("SUCCESS")){
                            result.put("weixinPayUrl", rootElt.elementText("code_url"));
                            result.put("prepayId", rootElt.elementText("prepay_id"));
                            result.put("status","success");
                            result.put("msg","success");
                            //成功，更改订单状态7(退款成功)
                            xcxOrderDao.upPtOrderStatusAndUpdateTime(xcxOrder.getOrderId(),7);
                            //发送短信
                            this.sendMsgRefund(xcxOrder);
                        }else{
                            result.put("status","false");
                            result.put("msg",rootElt.elementText("err_code_des"));
                        }
                        return result;
                    }
                    EntityUtils.consume(entity);
                }
                finally {
                    responseEntry.close();
                }
            }
            finally {
                httpclient.close();
            }
            return null;
        }catch(Exception e){
            e.printStackTrace();
            JSONObject result = new JSONObject();
            result.put("status","error");
            result.put("msg",e.getMessage());
            return result;
        }
    }

    public  String wxPayRefund(String out_trade_no,String total_fee) {
        StringBuffer xml = new StringBuffer();
        String data = null;
        try {
            String nonceStr = this.generateOrderNo();//获取随机字符串
            xml.append("</xml>");
            SortedMap<String,String> parameters = new TreeMap<String,String>();
            parameters.put("appid", WeChatConfigs.getAppID());//微信分配的公众账号ID
            parameters.put("mch_id", WeChatConfigs.getMchID());//商户号
            parameters.put("nonce_str", nonceStr);//随机字符串
            parameters.put("out_trade_no", out_trade_no);//商户订单号
            parameters.put("out_refund_no", nonceStr);//商户退款单号
            parameters.put("fee_type", "CNY");//币总
            parameters.put("total_fee", total_fee);//订单金额
            parameters.put("refund_fee", total_fee);//退款金额
            parameters.put("op_user_id", WeChatConfigs.getMchID());//商户号
            parameters.put("sign", createSign(parameters, WeChatConfigs.getApiSecret()));//API密钥
            data = PayCommonUtil.getRequestXml(parameters);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            return null;
        }
        return data;
    }

    public  String createSign(SortedMap<String, String> packageParams, String AppKey) {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + AppKey);
        String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
        return sign;
    }

    public String generateOrderNo() {
        String dateNo = DateUtils.formatOrderNoDate(new Date());
        return dateNo + RandomUtils.generatorOrderNo();
    }

    //退款，短信通知
    public String sendMsgRefund(XcxOrder xcxOrder) {
        //获取用户的手机号
        Customer customer = customerDao.getCustomerById(xcxOrder.getUserId());
        String str ="";
        if(customer != null){
            logger.info("发送短信开始，手机：{}", customer.getCustomerMobile());
            Map<String, String> map = new HashMap<String, String>();
            map.put("method", "sendMsg");
            map.put("username", "JSM40225");//此处填写用户账号
            map.put("password", "2z4jdhhr");//此处填写用户密码
            map.put("veryCode", "6ksjpzqkfiqu");
            map.put("mobile", customer.getCustomerMobile());//此处填写发送号码
            map.put("tempid", "JSM40225-0008");//此处填写模板短信编号
            map.put("content", "@1@=" + xcxOrder.getOrderPrice());//此处填写模板短信内容
            map.put("msgtype", "2");
            str = com.ibiaoke.common.utils.HttpPost.doPost("http://112.74.76.186:8030/service/httpService/httpInterface.do", map, "GBK");
            logger.info("发送短信结束，手机：{}，短信模板：{}，短信内容：{}。\n返回码:{}", customer.getCustomerMobile(), "JSM40225-0008", "好可惜，您的拼团队伍在24小时内人数未达标，您所支付的金额"+xcxOrder.getOrderPrice()+"元已原路返还", str);
            return str;
        }
        return str;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundPayByDeposit(XcxOrder xcxOrder) {
        //根据用户Id，更改（增加）可用预存款金额
        depositService.updatePrice(xcxOrder.getUserId(),xcxOrder.getOrderPrice());
        //增加当前用户流水记录 表bk_deposit_recorder
        depositRecorderService.insertByRefund(xcxOrder.getUserId(),3,xcxOrder.getOrderCode(),1,xcxOrder.getOrderPrice());
        //修改订单状态为7(退款成功)
        xcxOrderDao.upPtOrderStatusAndUpdateTime(xcxOrder.getOrderId(),7);
        //发送短信
        this.sendMsgRefund(xcxOrder);
    }

    @Transactional
    public Map xcxWxPay(String openId, String body, String out_trade_no, String total_fee, String trade_type, String notify_url){
        String nonceStr = PayCommonUtil.CreateNoncestr();
        //获取客户端的ip地址
        String spbill_create_ip = IPUtil.getIp();
        SortedMap<String, String> parameters = new TreeMap<String, String>();
        parameters.put("appid", WeChatConfigs.getXcxAppId());
        parameters.put("mch_id", WeChatConfigs.getMchID());
        parameters.put("nonce_str", nonceStr);
        parameters.put("body", body);
        parameters.put("out_trade_no", out_trade_no);
        parameters.put("total_fee", total_fee);
        parameters.put("spbill_create_ip", spbill_create_ip);
        parameters.put("notify_url", notify_url);
        parameters.put("trade_type", trade_type);
        parameters.put("openid", openId);
        /**
         * 设置签名
         */
        String sign = "";
        try {
             sign = WXPayUtil.generateSignature(parameters, WeChatConfigs.getApiSecret(), WXPayConstants.SignType.MD5);
        } catch (Exception e) {
            e.printStackTrace();
        }
        parameters.put("sign", sign);
        /**
         * 封装请求参数结束
         */
        String requestXML = PayCommonUtil.getRequestXml(parameters);
        /**
         * 调用统一下单接口
         */
        String result = PayCommonUtil.httpsRequest(AppWeChatConfigs.UNIFIED_ORDER_URL, "POST", requestXML);

        try {
            Map<String, String> map = XMLUtil.doXMLParse(result);

            String return_code = map.get("return_code");
            String return_msg = map.get("return_msg");

            if(return_code.equals("SUCCESS")){
                String prepay_id = map.get("prepay_id");
                map.put("nonceStr", nonceStr);
                map.put("package", "prepay_id=" + prepay_id);
                Long timeStamp = System.currentTimeMillis() / 1000;
                map.put("timeStamp", timeStamp + "");
                String stringSignTemp =
                         "appId=" + WeChatConfigs.getXcxAppId() +
                        "&nonceStr=" + nonceStr +
                        "&package=prepay_id=" + prepay_id+
                        "&signType=MD5&timeStamp=" + timeStamp;
                String paySign = PayUtil.sign(stringSignTemp, WeChatConfigs.getApiSecret(), "utf-8").toUpperCase();
                map.put("paySign", paySign);
            }
            map.put("appid", WeChatConfigs.getXcxAppId());
            map.put("signType","MD5");
            return map;
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Map<String, String> payDepositChargeByWe(TradeRecorder tradeRecorder, String openId) {
        AssertUtils.notNull(tradeRecorder,"请选择订单");
        validate1(tradeRecorder);
        String body ="让我来果源充值："+ tradeRecorder.getTradeNo();
        String out_trade_no = tradeRecorder.getTradeNo();
        String total_fee = String.valueOf(CashUtil.getCent(tradeRecorder.getMonney()));
        String trade_type = "JSAPI";
        String notify_url = GlobalConfigs.getConfigure(GlobalConfigsKey.WeChat.xcx_NOTIFY_URL);
        notify_url=notify_url.replace("BUSINESS_TYPE", BusinessDef.PAY_DEPOSIT);
        return xcxWxPay(openId, body, out_trade_no, total_fee, trade_type, notify_url);
    }

    public void validate1(TradeRecorder tradeRecorder) {
        AssertUtils.notNull(tradeRecorder, "请选择支付订单");
        AssertUtils.requireEquals(tradeRecorder.getStatus(), TradeStatusConsts.DEALING, "订单已支付");
        AssertUtils.requireTrue(tradeRecorder.getMonney().compareTo(new BigDecimal("0.01"))>=0, "支付金额不能小于0.01元");
    }
}
